package blue.http.internal.core.net;

import blue.http.core.HttpServer;
import blue.http.core.options.ServerOptions;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;

/**
 * @author Jin Zheng
 * @since 1.0 2021-08-16
 */
public class DefaultHttpServer implements HttpServer {
	private static Logger logger = LoggerFactory.getLogger(DefaultHttpServer.class);

	private final ServerOptions serverOptions;
	private final HttpInitializer initializer;
	private final int port;
	private final long startup;

	private Channel serverChannel;

	public DefaultHttpServer(DefaultHttpServerBuilder builder) {
		this.serverOptions = builder.getServerOptions();
		this.initializer = new HttpInitializer(builder);
		this.port = serverOptions.getPort();
		this.startup = serverOptions.getStartup();
	}

	@Override
	public void start() throws Exception {
		this.start(null);
	}

	@Override
	public void start(CountDownLatch latch) throws Exception {
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workerGroup = new NioEventLoopGroup(serverOptions.getIoThread());

		try {
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)
					.childHandler(initializer)
					.option(ChannelOption.SO_BACKLOG, 1024);

			ChannelFuture channelFuture = bootstrap.bind(port).sync();
			if (startup > 0) {
				logger.info("Http server started successful，listen port at {}，used {} ms.",
						port, System.currentTimeMillis() - startup);
			} else {
				logger.info("Http server started successful，listen port at {}.", port);
			}
			if (latch != null) {
				latch.countDown();
			}
			serverChannel = channelFuture.channel();
			serverChannel.closeFuture().sync();
			logger.info("Http server closed.");
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	@Override
	public void stop() {
		if (serverChannel != null) {
			serverChannel.close();
			serverChannel = null;
			logger.info("Http server closing...");
		}
	}
}
